using System;
using System.Collections;
using System.Collections.Specialized;
using System.Configuration;
using System.Reflection;
using log4net;
using Microsoft.Win32;

namespace DataBroker.Dal
{
    /// <summary>
    /// Utility class to retrieve configuration settings for the Data Access library.
    /// It is a thread-safe class, which is necessary for using under IIS/ASP.NET 
    /// </summary>
    public sealed class D2BkSettingsCollection : ICollection
    {
        private static readonly ILog Log =
            LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
        //private static NameValueCollection settings;

        private readonly static object dataBaseIndicatorLock = new object();

        private static Hashtable settings = Hashtable.Synchronized(new Hashtable());

        [ThreadStatic]
        private static string DataBaseIndicator;
        //private static String singleThreadDataBaseIndicator;
        /// <summary>
        /// Selects a database based on an indicator, used to choose the appropriate
        /// connection string from the configuration file.
        /// </summary>
        /// <param name="newDB">
        /// Indicator for database (ex: "P" for Production, "T" for Test, "D" for Development.
        /// </param>
        /// <returns></returns>
        public static string ChangeDatabase(string newDB)
        {
            if (newDB == null)
                newDB = "P";
            DataBaseIndicator = newDB;
            return newDB;
        }
        /// <summary>
        /// Returns the indicator for the database currently selected.
        /// </summary>
        /// <returns>char. "P" - Production; "T" - Test; "D" - Development</returns>
        public static string GetCurrentDatabase()
        {
            return DataBaseIndicator ?? "P";
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public static NameValueCollection Settings
        {
            get
            {
                string currentDb = GetCurrentDatabase();
                lock (dataBaseIndicatorLock)
                {
                    if (settings[currentDb] == null)
                    {
                        settings[currentDb] = GetConfig();
                    }
                    return (NameValueCollection)(settings[currentDb]);
                }
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="nvc"></param>
        /// <param name="sectionName"></param>
        /// <returns></returns>
        public static NameValueCollection SetSettings(NameValueCollection nvc, string sectionName)
        {
            string currentDb = GetCurrentDatabase();
            lock (dataBaseIndicatorLock)
            {
                settings[GetCurrentDatabase()] = nvc;
                NameValueCollection _settings = (NameValueCollection)(settings[currentDb]);
                D2BkConnection.RemoveCurrentConnection();
                return _settings;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="setting"></param>
        /// <param name="value"></param>
        /// <param name="sectionName"></param>
        /// <returns></returns>
        public static NameValueCollection SetSetting(string setting, string value, string sectionName)
        {
            string currentDb = GetCurrentDatabase();
            lock (dataBaseIndicatorLock)
            {
                NameValueCollection _settings;
                if (settings[currentDb] == null)
                {
                    Log.DebugFormat("No such settings for db: {0}", currentDb);
                    settings[currentDb] = GetConfig();
                }
                Log.DebugFormat("settings size for {0} : {1}", currentDb, settings.Count);
                Log.DebugFormat("Adding {0} = {1} for section {2}", setting, value, sectionName);
                ((NameValueCollection)settings[currentDb]).Remove(setting);
                ((NameValueCollection)settings[currentDb]).Add(setting, value);
                _settings = (NameValueCollection)(settings[currentDb]);
                D2BkConnection.RemoveCurrentConnection();
                return _settings;
            }
        }


        private static string GetSectionName()
        {
            string nodeName;
            if (GetCurrentDatabase() == "T")
                nodeName = "testAssemblySettings";
            else if (GetCurrentDatabase() == "P")
                nodeName = "productionAssemblySettings";
            else
                nodeName = GetCurrentDatabase();
            return nodeName;
        }

        /// <summary>
        /// Open and parse configuration file for specified assembly, returning collection 
        /// to caller for future use outside of this class.
        /// </summary>
        /// <returns></returns>
        public static NameValueCollection GetConfig()
        {
            String nodeName = GetSectionName();
            NameValueCollection config;
            NameValueCollection nvc = (NameValueCollection)ConfigurationManager.GetSection(nodeName);
            if (nvc != null)
            {
                config = new NameValueCollection(nvc.Count);
                foreach (string key in nvc.AllKeys)
                {
                    config.Add(key, nvc[key]);
                }
            }
            else
            {
                Log.DebugFormat("Adding new value collection, config section not found: {0}", nodeName);
                config = new NameValueCollection();
            }
            return config;
        }



        /// <summary>
        /// 
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public static string GetConfigKey(string key)
        {
            string configKey;
            D2BkSettingsCollection _settings = new D2BkSettingsCollection();
            configKey = _settings[key];
            return configKey;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public static string GetSettingFromRegistry(string key)
        {
            try
            {
                // Create registry key and named values
                RegistryKey rk = Registry.CurrentUser.OpenSubKey("Software", true);
                rk = rk.OpenSubKey("DataBroker");
                if (rk == null)
                {
                    return "";
                }
                rk = rk.OpenSubKey("DataAccessLayer");
                if (rk == null)
                {
                    return "";
                }
                rk = rk.OpenSubKey(GetCurrentDatabase());
                if (rk == null)
                {
                    return "";
                }
                string res = (string)rk.GetValue(key);
                if (res != null)
                    return res;

            }
            catch (System.Security.SecurityException ex)
            {
                Log.Error(ex.Message, ex);
            }
            catch (Exception ex)
            {
                Log.Error(ex.Message, ex);
            }
            return "";
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="setting"></param>
        /// <param name="key"></param>
        /// <returns></returns>
        public static string SetSettingToRegistry(string setting, string key)
        {
            try
            {
                // Create registry key and named values
                RegistryKey swrk = Registry.CurrentUser.OpenSubKey("Software", true);
                RegistryKey rk = swrk.OpenSubKey("DataBroker");
                if (rk == null)
                {
                    rk = swrk.CreateSubKey("DataBroker");
                    swrk = rk;
                }
                rk = rk.OpenSubKey("DataAccessLayer");
                if (rk == null)
                {
                    rk = swrk.CreateSubKey("DataAccessLayer");
                    swrk = rk;
                }
                rk = rk.OpenSubKey(GetCurrentDatabase());
                if (rk == null)
                {
                    rk = swrk.CreateSubKey(GetCurrentDatabase());
                }
                rk.SetValue(key, setting);
            }
            catch (System.Security.SecurityException ex)
            {
                Log.Error(ex.Message, ex);
            }
            catch (Exception ex)
            {
                Log.Error(ex.Message, ex);
            }
            return setting;
        }

        /// <summary>
        /// Property "this" to allow value to be retrieved as an array.
        /// </summary>
        public String this[String key]
        {
            get
            {
                String settingValue = ""; // GetSettingFromRegistry(key); 
                if (settingValue == "")
                {
                    NameValueCollection setings = Settings;
                    if (setings != null)
                    {
                        settingValue = setings[key];
                    }
                    if (settingValue == null)
                    {
                        return GetSettingFromRegistry(key);
                    }
                }
                return settingValue;
            }
        }
        #region ICollection method definitions
        void ICollection.CopyTo(Array array, int index)
        {
        }

        int ICollection.Count
        {
            get
            {
                return 0;
            }
        }

        bool ICollection.IsSynchronized
        {
            get
            {
                return true;
            }
        }

        object ICollection.SyncRoot
        {
            get
            {
                return null;
            }
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return null;
        }

        /// <summary>
        /// Provide the strongly typed member for ICollection.
        /// </summary>
        /// <param name="array"></param>
        /// <param name="index"></param>
        public void CopyTo(string[] array, int index)
        {
            ((ICollection)this).CopyTo(array, index);
        }

        #endregion

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public static string ReplacePassword(string password)
        {
            D2BkSettingsCollection setings = new D2BkSettingsCollection();
            string connectionString = setings["connectionString"];
            if (connectionString != "")
            {
                string[] parts = connectionString.Split(';');
                for (int i = 0; i < parts.Length; i++)
                {
                    if (parts[i].ToLower().Contains("pwd") || parts[i].ToLower().Contains("pwd"))
                    {
                        string[] pwd = parts[i].Split('=');
                        if (pwd.Length == 2)
                        {
                            pwd[1] = password;
                        }
                        parts[i] = string.Join("=", pwd);
                    }
                }
                connectionString = string.Join(";", parts);
            }
            SetSettingToRegistry(connectionString, "connectionString");
            return connectionString;
        }

    }
}